#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;



int except = 0;


////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::tradC() {
    fichierTradC.open("traduction.c", ios_base::in | ios_base::out | ios_base::trunc);
    if (fichierTradC.fail()) {
        cout << "Impossible de créer le fichier de traduction." << endl;
    }
    else {
    //    fichierTradC << "#include <iostream>" << endl;
        fichierTradC << "#include <stdio.h>" << endl;
        fichierTradC << "#include <stdlib.h>" << endl << endl;

        fichierTradC << "int main (int argc, char* argv[]) {" << endl;
        this->getArbre()->tradC(fichierTradC);
        fichierTradC<<"return 0;"<<endl;
        fichierTradC << "}" << endl;
        system ("rm traduction");
        system("gcc -Wall traduction.c -o traduction");
        //system("if [ $? -ne 0 ] then echo \"Compilation ratée\"  else    echo \"Compilation réussie\" ./traduction fi");
        system("./traduction");

        fichierTradC.close();

    }

}




////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");

    if (except) {
        int n = 2;
        throw n;
    }

    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>" ||
            ls.getSymCour() == "repeter" ||
            ls.getSymCour() == "tantque" ||
            ls.getSymCour() == "si" ||
            ls.getSymCour() == "lire" ||
            ls.getSymCour() == "ecrire" ||
            ls.getSymCour() == "pour");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instLire> | <instEcrire> | <instPour>


    if (ls.getSymCour() == "<VARIABLE>") {
        return affectation();
    } else if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "lire") {
        return instLire();
    } else if (ls.getSymCour() == "ecrire") {
        return instEcrire();
    } else if (ls.getSymCour() == "pour") {
        return instPour();
    } else
        erreur("<inst>");
    return NULL;


}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);




}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= [opUnaire] <terme> { <opAdd> <terme> }
    Symbole * opunaire = new Symbole("+");
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        *opunaire = opUnaire();
    }

    Noeud* term = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
        Noeud* terDroit = terme(); // lecture de l'operande droit
        term = new NoeudOperateurBinaire(operateur, term, terDroit); // const. du noeud
    }

    if (*opunaire == "+")
        return term;
    else return (new NoeudOperateurBinaire(*opunaire, NULL, term));



}



////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    //<terme> ::= <facteur> { <opMult> facteur> }
    Noeud* fact = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        Symbole operateur = opMult();
        Noeud* factDroit = facteur();
        fact = new NoeudOperateurBinaire(operateur, fact, factDroit);
    }
    return fact;
}


////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier> | <variable> | <opUnaire> <expBool> | ( <expBool> ) | <string> | <reel>

    Noeud* fact = NULL;

    if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>" || ls.getSymCour() == "<STRING>" || ls.getSymCour() == "<REEL>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        Symbole operateur = opUnaire();
        fact = expBool();
        // on représente le moins unaire (- facteur) par une soustraction binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(operateur, NULL, fact);
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}


////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opBinaire> ::= + | - 
{
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult()
// <opBinaire> ::= *  | /
{
    Symbole operateur;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() {
    //<expBool> ::= <relationTmp> { <opOu> <relationTmp> }
    Noeud* rel = relationTmp();
    while (ls.getSymCour() == "ou") {
        Symbole operateur = opOu();
        Noeud* relDroit = relationTmp();
        rel = new NoeudOperateurBinaire(operateur, rel, relDroit);
    }
    return rel;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relationTmp() {
    //  <relationTmp>::= <relation> { <opEt> <relation> }
    Noeud* rel = relation();
    while (ls.getSymCour() == "et") {
        Symbole operateur = opEt();
        Noeud* relDroit = relation();
        rel = new NoeudOperateurBinaire(operateur, rel, relDroit);
    }

    return rel;
}


////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opEt() {
    //<opBool> ::= et 

    Symbole operateur;
    if (ls.getSymCour() == "et") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opEt>");
    return operateur;

}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opOu() {
    //<opBool> ::= ou
    if (ls.getSymCour() == "ou")
        ls.suivant();
    else
        erreur("<opOu>");

    Symbole operateur;
    if (ls.getSymCour() == "ou") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opOut>");
    return operateur;

}



////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    //<relation> ::= <expression> { <opRel> <expression> }
    Noeud* exp = expression();
    while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
        Symbole operateur = opRel();
        Noeud* expDroit = expression();
        exp = new NoeudOperateurBinaire(operateur, exp, expDroit);
    }
    return exp;


}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    //<opRel> ::= == | != | < | <= | > | >=

    Symbole operateur;
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == ">" || ls.getSymCour() == "<" || ls.getSymCour() == "<=" || ls.getSymCour() == ">=") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opRel>");
    return operateur;

}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    //<opUnaire> ::= - | non
    Symbole operateur;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");
    return operateur;


}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    NoeudSi* nsi = new NoeudSi();
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    nsi->ajoute(cond, seq);
    while (ls.getSymCour() == "sinonsi") {
        ls.suivant();
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        nsi->ajoute(cond, seq);
    }
    if (ls.getSymCour() == "sinon") {
        ls.suivant();
        nsi->ajoute(NULL, seqInst());
    }
    sauterSymCour("finsi");
    return nsi;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() {
    //<instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* condition = expBool();
    sauterSymCour(")");
    Noeud* seqTq = seqInst();
    sauterSymCour("fintantque");
    return (new NoeudTq(condition, seqTq));
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    //<instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
    sauterSymCour("repeter");
    Noeud* seqRep = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* condition = expBool();
    sauterSymCour(")");
    return (new NoeudRepeter(seqRep, condition));
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire() {
    //<instLire> ::= lire ( <variable> ) 
    Noeud* var = NULL;


    sauterSymCour("lire");
    sauterSymCour("(");

    testerSymCour("<VARIABLE>");
    var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour(")");
    return (new NoeudLire(var));
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire() {
    //<instEcrire> ::= ecrire ( <expression> | <chaine> )
    Noeud* string = NULL;

    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<STRING>") {
        testerSymCour("<STRING>");
        string = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else {
        string = expression();
    }
    sauterSymCour(")");
    return (new NoeudEcrire(string));


}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() {
    //  <instPour>::= pour ( <VARIABLE> ; <relation> ; <affectation> ) <seqInst> finpour
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* var = NULL;
    if (ls.getSymCour() == "<VARIABLE>") {
        testerSymCour("<VARIABLE>");
        var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    }
    sauterSymCour(";");
    Noeud* condition = relation();
    sauterSymCour(";");
    Noeud* affectationBis = affectation();
    sauterSymCour(")");
    Noeud* seqPour = seqInst();
    sauterSymCour("finpour");

    return (new NoeudPour(var, condition, affectationBis, seqPour));
}


////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {


        try {
            int n = 2;
            throw n;
        } catch (int) {

            cout << endl << "-------- Erreur ligne " << ls.getLigne()
                    << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                    << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                    << endl;
            ls.suivant();
            except = 1;

        }
    }



}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {


    try {
        int n = 2;
        throw n;
    } catch (int) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
                << ls.getColonne() << endl << "   Attendu : " << mess << endl
                << "   Trouve  : " << ls.getSymCour() << endl << endl;

        except = 1;

    }

}
